
#include <main.h>
#include <graphics/rgIntersection.h>

// ray triangle 

// WARNING: test against front faced triangles only 
// from Fast, Minimum Storage Ray/Triangle Intersection Moller&Trumbore
bool rayTriTest (const rgVector& tri0, const rgVector& tri1, const rgVector& tri2, 
                 const rgVector& rayO, const rgVector& rayD, 
                 rgReal& t, rgReal& u, rgReal& v, rgReal& det)
{
	const rgVector edge1 = tri1 - tri0;
	const rgVector edge2 = tri2 - tri0;

	const rgVector pVec = rayD ^ edge2;
	det = edge1 * pVec;

    // ray is parallel to plane or comes from the back
    if (det < EPSILON) return rgFALSE;

    const rgVector tVec = rayO -tri0;
    u = tVec * pVec;
    if (u < static_cast<rgReal> (0) || u > det) return rgFALSE;

    const rgVector qVec =  tVec ^ edge1;
    v = rayD * qVec;
    if (v < static_cast<rgReal> (0) || u + v > det) return rgFALSE;

    t = edge2 * qVec;

	return rgTRUE;
}

// feed in rayTriIntersection return values
rgVector getRayTriPoint (const rgVector& rayO, const rgVector& rayD, const rgReal& t, const rgReal& det)
{
    return rayO + (t/det)* rayD;
}

// feed in rayTriIntersection return values
void getRayTriUV (rgReal& u, rgReal& v, const rgReal& det)
{
    const rgReal invDet = static_cast<rgReal> (1) / det;
    u *= invDet;
    v *= invDet;
}

// use getRayTriPoint like this (hint: same for getRayTriUV)
bool rayTriTestPoint(const rgVector& tri0, const rgVector& tri1, const rgVector& tri2, 
                     const rgVector& rayO, const rgVector& rayD,
                     rgVector& p)
{
    rgReal det, t, u, v;
    bool ret;
    if ( ret = rayTriTest(tri0, tri1, tri2, rayO, rayD, t, u, v, det) ) 
    {
        p = getRayTriPoint (rayO, rayD, t, det);
    }
    return ret;
}

// ----------------------------------------------------------------------------

// ray sphere
// from Real-Time Rendering 2nd ed
bool raySphereTest (const rgVector& c, const rgReal& r,
                    const rgVector& rayO, const rgVector& rayD,
                    rgReal& t)
{
    const rgVector& l = c - rayO;
    const rgReal s = l * rayD;
    const rgReal l2 = l*l;
    const rgReal r2 = r*r;
    
    // if sphere is behind and ray origin is outside sphere
    if ( s<static_cast<rgReal> (0) && l2 > r2) return rgFALSE; 

    const rgReal m2 = l2 - s*s;

    // if center-ray distance is greater than radius
    if(m2>r2) return rgFALSE;

    const rgReal q = sqrt(r2-m2);

    // if outside sphere get closest, otherwise get furthest point
    if(l2>r2) t=s-q;
    else t=s+q;

    return rgTRUE;
}

rgVector getRaySpherePoint (const rgVector& rayO, const rgVector& rayD,
                         const rgReal& t)
{
    return rayO + t*rayD;
}

bool raySphereTestPoint (const rgVector& c, const rgReal& r,
                         const rgVector& rayO, const rgVector& rayD,
                         rgVector& p)
{
    rgReal t;
    bool ret;
    if ( ret = raySphereTest (c, r, rayO, rayD, t) )
    {
        p = getRaySpherePoint (rayO, rayD, t);
    }
    return ret;
}


